Bogota car accidents 2014

library(tidyverse)
library(lubridate)

Data import

Data downloaded from https://docs.google.com/spreadsheets/d/1lgR-UpR9v9lmtoeK1ZhlVfs0FBj1xXz_eImhcrSI4Q0/

accidents_raw <- read_csv("data/Car_Accidents_2014_Bogota.csv")
Parsed with column specification:
cols(
  the_geom = col_character(),
  cartodb_id = col_integer(),
  codigo_accidente = col_integer(),
  fecha_ocurrencia = col_date(format = ""),
  hora_ocurrencia = col_character(),
  gravedad = col_character(),
  objectid = col_integer(),
  localidad = col_character(),
  municipio = col_character(),
  x = col_double(),
  y = col_double(),
  timedate = col_datetime(format = ""),
  clase = col_character()
)
accidents_raw
  • the_geom : geom
  • cartodb_id ???
  • codigo_accidente ??? : Accident code from police ?
  • fecha_ocurrencia 2014-10-29 : Date
  • hora_occurencia ??? 14:10:00 : Time
  • gravedad : Gravity (severity)
  • objectid ???
  • localidad : Location
  • municipio : Municipality
  • x : Longitude
  • y : Latitude
  • timedate <S3: POSIXct> 2014-10-29 14:10:00
  • clase : Class [choque = shock; atropello = ; otro = ; caida de ocupante = ]
# The data is for one year (2014). If we want to filter by month (etc), need a `month` variable first. Could use **lubridate**, or `separate()` ...The former avoids 10/11/12 coming before 2/3/4/etc, but the latter should also be ok in plots if using integers
?separate
accidents <- accidents_raw %>%
  separate(fecha_ocurrencia, c("year", "month", "day"), "-", convert = TRUE) %>%
                                              # `convert` gets y&m&d as integers
    filter(year == 2014)                      # 17 rows from 2013 filtered out
accidents <- accidents %>%
  separate(hora_ocurrencia, c("hour", "minute", "second"), ":", convert = TRUE) %>%
    filter(hour != "1899-12-30")              # 9 rows filtered out
Too few values at 9 locations: 323, 773, 3594, 4071, 5378, 6861, 7874, 8184, 9044
accidents                                     # (9973 rows x 17 cols)

Basic EDA

# Just to get a sense of what's going on
accidents %>% count(the_geom)            # 6921 rows (so not unique)
accidents %>% count(the_geom) %>% count(n)  # 21 rows (1 to 22, without 18)
accidents %>% count(cartodb_id)          # 9973 rows (confirmed as unique)
accidents %>% count(codigo_accidente)    # 9973 rows (unique). Seem to be 5e+05 or 4e+06
accidents %>% count(month)               # 12 rows. `n` is 485-1584. Higher in Oct/Nov/Dec
accidents %>% count(hour)                # 24 rows. `n` is 51-722 (2am, 2pm)
accidents %>% count(gravedad)            # 3 rows. "CON HERIDOS", "CON MUERTOS", "SOLO DANOS"
accidents %>% count(objectid)            # 9973 rows (confirmed as unique)
accidents %>% count(localidad)           # 19 rows. `n` is 52-1176
accidents %>% count(municipio)           # 1 row ("BOGOTA"). `n` is 9973
accidents %>% count(clase)               # 7 rows. `n` is 1 ("INCENDIO") to 8494 ("CHOQUE")

Initial data visialisation, to see if there might be a location/ time pattern

accidents <- accidents %>%
  mutate(date = make_date(year, month, day), week_no = week(date), week_day = wday(date, label = TRUE), hour = as.numeric(hour), n = n())
accidents
ggplot(accidents, aes(date)) +
  geom_histogram() +
  facet_wrap(~ localidad)

ggplot(accidents, aes(x, y, colour = localidad)) +
  geom_point()

Maybe look at a time-based model (especially if there is data for other years too, in which case start with localidad=ENGATIVA). And/or a visualisation (like ACotgreave? if I can get week#). Or time of day (model/viz). Or connected to streetlights (if there’s a joining field)

accidents2 <- accidents %>%
  group_by(week_no, week_day) %>%
  summarise(n = n())
accidents2
ggplot(accidents2, aes(week_day, week_no)) +
  geom_point(aes(colour = n, size = n, alpha = 0.5))

ggplot(accidents2, aes(week_no, week_day)) +
  geom_point(aes(colour = n, size = n, alpha = 0.5))

accidents3 <- accidents %>%
  group_by(week_day, hour) %>%
  summarise(n = n())
accidents3
ggplot(accidents3, aes(week_day, hour)) +
  geom_point(aes(colour = n, size = n, alpha = 0.5))

# Create a factor, to order the hours correctly (0-23) on the plot axis
# library(forcats)
# hour_levels <- c("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23")
# y1 <- factor(hour, levels = hour_levels)
# Couldn't fix an error here. Instead converted `hour` in `accidents` from <chr> to numeric
# y1
# ggplot(accidents3, aes(week_day, y1)) +
#   geom_point(aes(colour = n, size = n, alpha = 0.5))
#--
# ggplot(accidents3, aes(hour, n)) +
#   geom_density(aes(colour = week_day)) +
#   facet_wrap(~week_day)
# Error in eval(substitute(list(...)), `_data`, parent.frame()) : object 'y' not found
accidents4 <- accidents %>%
  group_by(week_day, hour, localidad) %>%  # error: grouping gives rings on viz not total
  summarise(n = n())
accidents4
ggplot(accidents4, aes(week_day, hour)) +
  geom_point(aes(colour = n, size = n, alpha = 0.5)) +
    facet_wrap(~ localidad)

ggplot(accidents4, aes(week_day, hour)) +
  geom_point(aes(colour = n, size = n, alpha = 0.5)) +
    facet_wrap(~ month)
Error in combine_vars(data, params$plot_env, vars, drop = params$drop) : 
  At least one layer must contain all variables used for facetting

Try to create model of variation by hour. Start with localidad=ENGATIVA

accidents5 <- accidents %>%
  group_by(hour) %>%
  summarise(n = n())
accidents5
ggplot(accidents5, aes(hour, n)) +
  geom_line()

ggplot(accidents3, aes(hour, n)) +
  geom_line(aes(colour = week_day))

ggplot(accidents3, aes(hour, n)) +
  geom_line(aes(colour = week_day)) +
  facet_wrap(~week_day)

ggplot(accidents3, aes(hour, n)) +
  geom_point(aes(colour = week_day)) +
  facet_wrap(~week_day)

# ggplot(accidents4, aes(hour, n)) +
#  geom_line(aes(colour = week_day)) +
#  facet_grid(. ~ week_day)
# "Error in combine_vars(data, params$plot_env, cols, drop = params$drop) : At least one layer must contain all variables used for facetting""
LS0tCnRpdGxlOiAiQm9nb3RhIGNhciBhY2NpZGVudHMgMjAxNCIKb3V0cHV0OgogIGdpdGh1Yl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgojIEJvZ290YSBjYXIgYWNjaWRlbnRzIDIwMTQKCmBgYHtyIHNldHVwfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCmBgYAoKCiMjIERhdGEgaW1wb3J0CgpEYXRhIGRvd25sb2FkZWQgZnJvbSBodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xbGdSLVVwUjl2OWxtdG9lSzFaaGxWZnMwRkJqMXhYel9lSW1oY3JTSTRRMC8KCmBgYHtyfQphY2NpZGVudHNfcmF3IDwtIHJlYWRfY3N2KCJkYXRhL0Nhcl9BY2NpZGVudHNfMjAxNF9Cb2dvdGEuY3N2IikKYWNjaWRlbnRzX3JhdwpgYGAKCiogYHRoZV9nZW9tYCA8Y2hyPiA6IGdlb20KKiBgY2FydG9kYl9pZGAgPGludD4gICAgICAgID8/PwoqIGBjb2RpZ29fYWNjaWRlbnRlYCA8aW50PiAgPz8/IDogQWNjaWRlbnQgY29kZSBmcm9tIHBvbGljZSA/CiogYGZlY2hhX29jdXJyZW5jaWFgIDxkYXRlPiAgICAgICAyMDE0LTEwLTI5IDogRGF0ZQoqIGBob3JhX29jY3VyZW5jaWFgIDxjaHI+ICAgPz8/ICAgMTQ6MTA6MDAgOiBUaW1lCiogYGdyYXZlZGFkYCA8Y2hyPiA6IEdyYXZpdHkgKHNldmVyaXR5KQoqIGBvYmplY3RpZGAgPGludD4gICAgICAgICAgPz8/CiogYGxvY2FsaWRhZGAgPGNocj4gOiBMb2NhdGlvbgoqIGBtdW5pY2lwaW9gIDxjaHI+IDogTXVuaWNpcGFsaXR5CiogYHhgIDxkYmw+IDogTG9uZ2l0dWRlCiogYHlgIDxkYmw+IDogTGF0aXR1ZGUKKiBgdGltZWRhdGVgIDxTMzogUE9TSVhjdD4gICAgICAgIDIwMTQtMTAtMjkgMTQ6MTA6MDAKKiBgY2xhc2VgIDxjaHI+IDogQ2xhc3MgW2Nob3F1ZSA9IHNob2NrOyBhdHJvcGVsbG8gPSA7IG90cm8gPSA7IGNhaWRhIGRlIG9jdXBhbnRlID0gXQoKCgoKYGBge3J9CiMgVGhlIGRhdGEgaXMgZm9yIG9uZSB5ZWFyICgyMDE0KS4gSWYgd2Ugd2FudCB0byBmaWx0ZXIgYnkgbW9udGggKGV0YyksIG5lZWQgYSBgbW9udGhgIHZhcmlhYmxlIGZpcnN0LiBDb3VsZCB1c2UgKipsdWJyaWRhdGUqKiwgb3IgYHNlcGFyYXRlKClgIC4uLlRoZSBmb3JtZXIgYXZvaWRzIDEwLzExLzEyIGNvbWluZyBiZWZvcmUgMi8zLzQvZXRjLCBidXQgdGhlIGxhdHRlciBzaG91bGQgYWxzbyBiZSBvayBpbiBwbG90cyBpZiB1c2luZyBpbnRlZ2VycwoKP3NlcGFyYXRlCgphY2NpZGVudHMgPC0gYWNjaWRlbnRzX3JhdyAlPiUKICBzZXBhcmF0ZShmZWNoYV9vY3VycmVuY2lhLCBjKCJ5ZWFyIiwgIm1vbnRoIiwgImRheSIpLCAiLSIsIGNvbnZlcnQgPSBUUlVFKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgYGNvbnZlcnRgIGdldHMgeSZtJmQgYXMgaW50ZWdlcnMKICAgIGZpbHRlcih5ZWFyID09IDIwMTQpICAgICAgICAgICAgICAgICAgICAgICMgMTcgcm93cyBmcm9tIDIwMTMgZmlsdGVyZWQgb3V0CgphY2NpZGVudHMgPC0gYWNjaWRlbnRzICU+JQogIHNlcGFyYXRlKGhvcmFfb2N1cnJlbmNpYSwgYygiaG91ciIsICJtaW51dGUiLCAic2Vjb25kIiksICI6IiwgY29udmVydCA9IFRSVUUpICU+JQogICAgZmlsdGVyKGhvdXIgIT0gIjE4OTktMTItMzAiKSAgICAgICAgICAgICAgIyA5IHJvd3MgZmlsdGVyZWQgb3V0CgphY2NpZGVudHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAoOTk3MyByb3dzIHggMTcgY29scykKYGBgCgoKIyMgQmFzaWMgRURBCgpgYGB7cn0KIyBKdXN0IHRvIGdldCBhIHNlbnNlIG9mIHdoYXQncyBnb2luZyBvbgoKYWNjaWRlbnRzICU+JSBjb3VudCh0aGVfZ2VvbSkgICAgICAgICAgICAjIDY5MjEgcm93cyAoc28gbm90IHVuaXF1ZSkKYWNjaWRlbnRzICU+JSBjb3VudCh0aGVfZ2VvbSkgJT4lIGNvdW50KG4pICAjIDIxIHJvd3MgKDEgdG8gMjIsIHdpdGhvdXQgMTgpCmFjY2lkZW50cyAlPiUgY291bnQoY2FydG9kYl9pZCkgICAgICAgICAgIyA5OTczIHJvd3MgKGNvbmZpcm1lZCBhcyB1bmlxdWUpCmFjY2lkZW50cyAlPiUgY291bnQoY29kaWdvX2FjY2lkZW50ZSkgICAgIyA5OTczIHJvd3MgKHVuaXF1ZSkuIFNlZW0gdG8gYmUgNWUrMDUgb3IgNGUrMDYKYWNjaWRlbnRzICU+JSBjb3VudChtb250aCkgICAgICAgICAgICAgICAjIDEyIHJvd3MuIGBuYCBpcyA0ODUtMTU4NC4gSGlnaGVyIGluIE9jdC9Ob3YvRGVjCmFjY2lkZW50cyAlPiUgY291bnQoaG91cikgICAgICAgICAgICAgICAgIyAyNCByb3dzLiBgbmAgaXMgNTEtNzIyICgyYW0sIDJwbSkKYWNjaWRlbnRzICU+JSBjb3VudChncmF2ZWRhZCkgICAgICAgICAgICAjIDMgcm93cy4gIkNPTiBIRVJJRE9TIiwgIkNPTiBNVUVSVE9TIiwgIlNPTE8gREFOT1MiCmFjY2lkZW50cyAlPiUgY291bnQob2JqZWN0aWQpICAgICAgICAgICAgIyA5OTczIHJvd3MgKGNvbmZpcm1lZCBhcyB1bmlxdWUpCmFjY2lkZW50cyAlPiUgY291bnQobG9jYWxpZGFkKSAgICAgICAgICAgIyAxOSByb3dzLiBgbmAgaXMgNTItMTE3NgphY2NpZGVudHMgJT4lIGNvdW50KG11bmljaXBpbykgICAgICAgICAgICMgMSByb3cgKCJCT0dPVEEiKS4gYG5gIGlzIDk5NzMKYWNjaWRlbnRzICU+JSBjb3VudChjbGFzZSkgICAgICAgICAgICAgICAjIDcgcm93cy4gYG5gIGlzIDEgKCJJTkNFTkRJTyIpIHRvIDg0OTQgKCJDSE9RVUUiKQpgYGAKCkluaXRpYWwgZGF0YSB2aXNpYWxpc2F0aW9uLCB0byBzZWUgaWYgdGhlcmUgbWlnaHQgYmUgYSBsb2NhdGlvbi8gdGltZSBwYXR0ZXJuCgpgYGB7cn0KYWNjaWRlbnRzIDwtIGFjY2lkZW50cyAlPiUKICBtdXRhdGUoZGF0ZSA9IG1ha2VfZGF0ZSh5ZWFyLCBtb250aCwgZGF5KSwgd2Vla19ubyA9IHdlZWsoZGF0ZSksIHdlZWtfZGF5ID0gd2RheShkYXRlLCBsYWJlbCA9IFRSVUUpLCBob3VyID0gYXMubnVtZXJpYyhob3VyKSwgbiA9IG4oKSkKCmFjY2lkZW50cwoKZ2dwbG90KGFjY2lkZW50cywgYWVzKGRhdGUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKSArCiAgZmFjZXRfd3JhcCh+IGxvY2FsaWRhZCkKCmdncGxvdChhY2NpZGVudHMsIGFlcyh4LCB5LCBjb2xvdXIgPSBsb2NhbGlkYWQpKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKCk1heWJlIGxvb2sgYXQgYSB0aW1lLWJhc2VkIG1vZGVsIChlc3BlY2lhbGx5IGlmIHRoZXJlIGlzIGRhdGEgZm9yIG90aGVyIHllYXJzIHRvbywgaW4gd2hpY2ggY2FzZSBzdGFydCB3aXRoIGxvY2FsaWRhZD1FTkdBVElWQSkuIEFuZC9vciBhIHZpc3VhbGlzYXRpb24gKGxpa2UgQUNvdGdyZWF2ZT8gaWYgSSBjYW4gZ2V0IHdlZWsjKS4gT3IgdGltZSBvZiBkYXkgKG1vZGVsL3ZpeikuIE9yIGNvbm5lY3RlZCB0byBzdHJlZXRsaWdodHMgKGlmIHRoZXJlJ3MgYSBqb2luaW5nIGZpZWxkKQoKYGBge3J9CmFjY2lkZW50czIgPC0gYWNjaWRlbnRzICU+JQogIGdyb3VwX2J5KHdlZWtfbm8sIHdlZWtfZGF5KSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSkKCmFjY2lkZW50czIKCmdncGxvdChhY2NpZGVudHMyLCBhZXMod2Vla19kYXksIHdlZWtfbm8pKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gbiwgc2l6ZSA9IG4sIGFscGhhID0gMC41KSkKCmdncGxvdChhY2NpZGVudHMyLCBhZXMod2Vla19ubywgd2Vla19kYXkpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gbiwgc2l6ZSA9IG4sIGFscGhhID0gMC41KSkKYGBgCgoKYGBge3J9CmFjY2lkZW50czMgPC0gYWNjaWRlbnRzICU+JQogIGdyb3VwX2J5KHdlZWtfZGF5LCBob3VyKSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSkKCmFjY2lkZW50czMKCmdncGxvdChhY2NpZGVudHMzLCBhZXMod2Vla19kYXksIGhvdXIpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gbiwgc2l6ZSA9IG4sIGFscGhhID0gMC41KSkKCiMgQ3JlYXRlIGEgZmFjdG9yLCB0byBvcmRlciB0aGUgaG91cnMgY29ycmVjdGx5ICgwLTIzKSBvbiB0aGUgcGxvdCBheGlzCiMgbGlicmFyeShmb3JjYXRzKQoKIyBob3VyX2xldmVscyA8LSBjKCIwIiwgIjEiLCAiMiIsICIzIiwgIjQiLCAiNSIsICI2IiwgIjciLCAiOCIsICI5IiwgIjEwIiwgIjExIiwgIjEyIiwgIjEzIiwgIjE0IiwgIjE1IiwgIjE2IiwgIjE3IiwgIjE4IiwgIjE5IiwgIjIwIiwgIjIxIiwgIjIyIiwgIjIzIikKCiMgeTEgPC0gZmFjdG9yKGhvdXIsIGxldmVscyA9IGhvdXJfbGV2ZWxzKQojIENvdWxkbid0IGZpeCBhbiBlcnJvciBoZXJlLiBJbnN0ZWFkIGNvbnZlcnRlZCBgaG91cmAgaW4gYGFjY2lkZW50c2AgZnJvbSA8Y2hyPiB0byBudW1lcmljCiMgeTEKCiMgZ2dwbG90KGFjY2lkZW50czMsIGFlcyh3ZWVrX2RheSwgeTEpKSArCiMgICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBuLCBzaXplID0gbiwgYWxwaGEgPSAwLjUpKQoKIy0tCgojIGdncGxvdChhY2NpZGVudHMzLCBhZXMoaG91ciwgbikpICsKIyAgIGdlb21fZGVuc2l0eShhZXMoY29sb3VyID0gd2Vla19kYXkpKSArCiMgICBmYWNldF93cmFwKH53ZWVrX2RheSkKIyAiRXJyb3IgaW4gZXZhbChzdWJzdGl0dXRlKGxpc3QoLi4uKSksIGBfZGF0YWAsIHBhcmVudC5mcmFtZSgpKSA6IG9iamVjdCAneScgbm90IGZvdW5kIiIKYGBgCgoKYGBge3J9CmFjY2lkZW50czQgPC0gYWNjaWRlbnRzICU+JQogIGdyb3VwX2J5KG1vbnRoLCB3ZWVrX2RheSwgaG91ciwgbG9jYWxpZGFkKSAlPiUgICMgZXJyb3I6IGdyb3VwaW5nIGdpdmVzIHJpbmdzIG9uIHZpeiBub3QgdG90YWwKICBzdW1tYXJpc2UobiA9IG4oKSkKCmFjY2lkZW50czQKCmdncGxvdChhY2NpZGVudHM0LCBhZXMod2Vla19kYXksIGhvdXIpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gbiwgc2l6ZSA9IG4sIGFscGhhID0gMC41KSkgKwogICAgZmFjZXRfd3JhcCh+IGxvY2FsaWRhZCkKCmdncGxvdChhY2NpZGVudHM0LCBhZXMod2Vla19kYXksIGhvdXIpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID0gbiwgc2l6ZSA9IG4sIGFscGhhID0gMC41KSkgKwogICAgZmFjZXRfd3JhcCh+IG1vbnRoKQoKZ2dwbG90KGFjY2lkZW50czQsIGFlcyh3ZWVrX2RheSwgaG91cikpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBuLCBzaXplID0gbiwgYWxwaGEgPSAwLjUpKSArCiAgICBmYWNldF9ncmlkKGxvY2FsaWRhZCB+IG1vbnRoKQoKZ2dwbG90KGFjY2lkZW50czQsIGFlcyh3ZWVrX2RheSwgaG91cikpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBuLCBzaXplID0gbiwgYWxwaGEgPSAwLjUpKSArCiAgICBmYWNldF9ncmlkKGxvY2FsaWRhZCB+IG1vbnRoKQpgYGAKCgpUcnkgdG8gY3JlYXRlIG1vZGVsIG9mIHZhcmlhdGlvbiBieSBob3VyLiBTdGFydCB3aXRoIGxvY2FsaWRhZD1FTkdBVElWQQoKYGBge3J9CmFjY2lkZW50czUgPC0gYWNjaWRlbnRzICU+JQogIGdyb3VwX2J5KGhvdXIpICU+JQogIHN1bW1hcmlzZShuID0gbigpKQoKYWNjaWRlbnRzNQoKZ2dwbG90KGFjY2lkZW50czUsIGFlcyhob3VyLCBuKSkgKwogIGdlb21fbGluZSgpCgpnZ3Bsb3QoYWNjaWRlbnRzMywgYWVzKGhvdXIsIG4pKSArCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXIgPSB3ZWVrX2RheSkpCgpnZ3Bsb3QoYWNjaWRlbnRzMywgYWVzKGhvdXIsIG4pKSArCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXIgPSB3ZWVrX2RheSkpICsKICBmYWNldF93cmFwKH53ZWVrX2RheSkKCmdncGxvdChhY2NpZGVudHMzLCBhZXMoaG91ciwgbikpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSB3ZWVrX2RheSkpICsKICBmYWNldF93cmFwKH53ZWVrX2RheSkKCiMgZ2dwbG90KGFjY2lkZW50czQsIGFlcyhob3VyLCBuKSkgKwojICBnZW9tX2xpbmUoYWVzKGNvbG91ciA9IHdlZWtfZGF5KSkgKwojICBmYWNldF9ncmlkKC4gfiB3ZWVrX2RheSkKIyAiRXJyb3IgaW4gY29tYmluZV92YXJzKGRhdGEsIHBhcmFtcyRwbG90X2VudiwgY29scywgZHJvcCA9IHBhcmFtcyRkcm9wKSA6IEF0IGxlYXN0IG9uZSBsYXllciBtdXN0IGNvbnRhaW4gYWxsIHZhcmlhYmxlcyB1c2VkIGZvciBmYWNldHRpbmciIgpgYGAKCgoK